import { computed, ref } from "vue";

export type SortOrder = "ascending" | "descending" | null;

export interface PureTablePlusProps {
  data: any[];
  columns: any[];
  pageSizes?: number[];
  /**
   * Initial page size. Not two-way bound. Used only as the default value.
   */
  pageSize?: number;
  defaultSort?: { prop: string; order: SortOrder };
  searchProp?: string;
  searchPlaceholder?: string;
  height?: string | null;
}

export interface PureTablePlusEmits {
  (
    e: "sort-change",
    payload: { column: any; prop: string; order: SortOrder }
  ): void;
  (e: "filter-change", payload: Record<string, string[]>): void;
}

export const usePureTablePlus = (
  props: Readonly<PureTablePlusProps>,
  emit: PureTablePlusEmits
) => {
  const internalSearch = ref("");

  const internalPageSize = ref(props.pageSize ?? 20);
  const internalCurrentPage = ref(1);

  // sort state
  const sortProp = ref<string>(props.defaultSort?.prop ?? "");
  const sortOrder = ref<SortOrder>(props.defaultSort?.order ?? null);

  // normalize columns: enable custom sort when sortable or sortMethod provided
  const normalizedColumns = computed(() => {
    return (props.columns || []).map(col => {
      if (col && (col.sortable || typeof col.sortMethod === "function")) {
        return { ...col, sortable: "custom" };
      }
      return col;
    });
  });

  const pageSizesComputed = computed(
    () => props.pageSizes ?? [10, 20, 50, 100]
  );

  const fullData = computed(() => props.data ?? []);

  const searchedData = computed(() => {
    const source = fullData.value;
    const key = props.searchProp;
    const q = internalSearch.value?.trim();
    if (!key || !q) return source;
    const lower = q.toLowerCase();
    return source.filter(row => {
      const val = row?.[key];
      if (val == null) return false;
      return String(val).toLowerCase().includes(lower);
    });
  });

  const columnSortMethodMap = computed<
    Record<string, ((a: any, b: any) => number) | undefined>
  >(() => {
    const map: Record<string, ((a: any, b: any) => number) | undefined> = {};
    for (const col of props.columns || []) {
      if (col?.prop) {
        map[col.prop] = col.sortMethod;
      }
    }
    return map;
  });

  const sortedData = computed(() => {
    const data = searchedData.value.slice();
    if (!sortProp.value || !sortOrder.value) return data;
    const prop = sortProp.value;
    const order = sortOrder.value;
    const method = columnSortMethodMap.value[prop];

    const comparator = (a: any, b: any) => {
      if (typeof method === "function") {
        const res = method(a, b);
        return order === "ascending" ? res : -res;
      }
      const av = a?.[prop];
      const bv = b?.[prop];
      if (av == null && bv == null) return 0;
      if (av == null) return order === "ascending" ? -1 : 1;
      if (bv == null) return order === "ascending" ? 1 : -1;
      if (typeof av === "number" && typeof bv === "number") {
        return order === "ascending" ? av - bv : bv - av;
      }
      const sa = String(av);
      const sb = String(bv);
      const cmp = sa.localeCompare(sb);
      return order === "ascending" ? cmp : -cmp;
    };

    return data.sort(comparator);
  });

  const pagedData = computed(() => {
    const start = (internalCurrentPage.value - 1) * internalPageSize.value;
    return sortedData.value.slice(start, start + internalPageSize.value);
  });

  const onSortChange = (payload: {
    column: any;
    prop: string;
    order: SortOrder;
  }) => {
    sortProp.value = payload?.prop ?? "";
    sortOrder.value = payload?.order ?? null;
    emit("sort-change", payload);
  };

  const onFilterChange = (filters: Record<string, string[]>) => {
    emit("filter-change", filters);
    internalCurrentPage.value = 1;
  };

  const onPageSizeChange = (size: number) => {
    internalPageSize.value = size;
    internalCurrentPage.value = 1;
  };

  const onCurrentPageChange = (page: number) => {
    internalCurrentPage.value = page;
  };

  const tableHeight = computed(() => {
    const h = props.height
      ? `${Number(props.height.replace("px", "")) - 66}px`
      : null;
    return h;
  });

  return {
    // state
    internalSearch,
    internalPageSize,
    internalCurrentPage,
    sortProp,
    sortOrder,
    tableHeight,
    // data
    normalizedColumns,
    pageSizesComputed,
    searchedData,
    pagedData,
    // handlers
    onSortChange,
    onFilterChange,
    onPageSizeChange,
    onCurrentPageChange
  };
};
